package com.lemon.exam.common.web;

import com.lemon.exam.common.enums.ServiceEnum;
import com.lemon.exam.common.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;

import java.net.URI;
import java.time.Duration;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * 请求对象
 *
 * @author Lemon
 * @since 2025/11/2 13:29
 */
@Slf4j
public final class Request {
    // WebClient 请求对象
    private static final WebClient webClient = WebClient.builder().build();

    // 空请求头
    private static final Consumer<HttpHeaders> EMPTY_HEADERS = headers -> {
    };

    /**
     * GET 请求
     *
     * @param serviceEnum
     * @param uri
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Mono<T> get(ServiceEnum serviceEnum, String uri, Class<T> tClass) {
        return get(serviceEnum, uri, null, tClass);
    }

    /**
     * GET 请求
     *
     * @param serviceEnum
     * @param uri
     * @param headers
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Mono<T> get(ServiceEnum serviceEnum, String uri, Consumer<HttpHeaders> headers, Class<T> tClass) {
        log.info("GET请求 info:{},uri:{}", serviceEnum.getInfo(), uri);
        return getFullUrl(serviceEnum, uri)
                .flatMap(fullUrl -> webClient.get()
                        .uri(fullUrl)
                        .headers(headers == null ? EMPTY_HEADERS : headers)
                        .retrieve()
                        .bodyToMono(tClass)
                        .doOnSuccess(response -> log.info("请求成功: {}, URL: {}", response, fullUrl))
                        .doOnError(error -> log.error("请求失败: {}, URL: {}", error.getMessage(), fullUrl)));
    }

    /**
     * POST 请求
     *
     * @param serviceEnum
     * @param uri
     * @param body
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Mono<T> post(ServiceEnum serviceEnum, String uri, Object body, Class<T> tClass) {
        return post(serviceEnum, uri, body, null, tClass);
    }

    /**
     * POST 请求
     *
     * @param serviceEnum
     * @param uri
     * @param body
     * @param headers
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Mono<T> post(ServiceEnum serviceEnum, String uri, Object body, Consumer<HttpHeaders> headers, Class<T> tClass) {
        log.info("POST请求 info:{},uri:{}", serviceEnum.getInfo(), uri);
        return getFullUrl(serviceEnum, uri)
                .flatMap(fullUrl -> webClient.post()
                        .uri(fullUrl)
                        .headers(headers == null ? EMPTY_HEADERS : headers)
                        .bodyValue(body)
                        .retrieve()
                        .bodyToMono(tClass)
                        .doOnSuccess(response -> log.info("请求成功: {}, URL: {}", response, fullUrl))
                        .doOnError(error -> log.error("请求失败: {}, URL: {}", error.getMessage(), fullUrl)));
    }

    /**
     * PUT 请求
     *
     * @param serviceEnum
     * @param uri
     * @param body
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Mono<T> put(ServiceEnum serviceEnum, String uri, Object body, Class<T> tClass) {
        return put(serviceEnum, uri, body, null, tClass);
    }

    /**
     * PUT 请求
     *
     * @param serviceEnum
     * @param uri
     * @param body
     * @param headers
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Mono<T> put(ServiceEnum serviceEnum, String uri, Object body, Consumer<HttpHeaders> headers, Class<T> tClass) {
        log.info("PUT请求 info:{},uri:{}", serviceEnum.getInfo(), uri);
        return getFullUrl(serviceEnum, uri)
                .flatMap(fullUrl -> webClient.put()
                        .uri(fullUrl)
                        .headers(headers == null ? EMPTY_HEADERS : headers)
                        .bodyValue(body)
                        .retrieve()
                        .bodyToMono(tClass)
                        .doOnSuccess(response -> log.info("请求成功: {}, URL: {}", response, fullUrl))
                        .doOnError(error -> log.error("请求失败: {}, URL: {}", error.getMessage(), fullUrl)));
    }

    /**
     * DELETE 请求
     *
     * @param serviceEnum
     * @param uri
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Mono<T> delete(ServiceEnum serviceEnum, String uri, Class<T> tClass) {
        return delete(serviceEnum, uri, null, tClass);
    }

    /**
     * DELETE 请求
     *
     * @param serviceEnum
     * @param uri
     * @param headers
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Mono<T> delete(ServiceEnum serviceEnum, String uri, Consumer<HttpHeaders> headers, Class<T> tClass) {
        log.info("DELETE请求 info:{},uri:{}", serviceEnum.getInfo(), uri);
        return getFullUrl(serviceEnum, uri)
                .flatMap(fullUrl -> webClient.delete()
                        .uri(fullUrl)
                        .headers(headers == null ? EMPTY_HEADERS : headers)
                        .retrieve()
                        .bodyToMono(tClass)
                        .doOnSuccess(response -> log.info("请求成功: {}, URL: {}", response, fullUrl))
                        .doOnError(error -> log.error("请求失败: {}, URL: {}", error.getMessage(), fullUrl)));
    }

    /**
     * SSE 订阅请求
     *
     * @param serviceEnum
     * @param uri
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Flux<T> sse(ServiceEnum serviceEnum, String uri, Object body, Class<T> tClass) {
        return sse(serviceEnum, uri, body, null, tClass);
    }

    /**
     * SSE 订阅请求
     *
     * @param serviceEnum
     * @param uri
     * @param headers
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> Flux<T> sse(ServiceEnum serviceEnum, String uri, Object body, Consumer<HttpHeaders> headers, Class<T> tClass) {
        log.info("SSE请求 info:{},uri:{}", serviceEnum.getInfo(), uri);
        return getFullUrl(serviceEnum, uri)
                .flatMapMany(fullUrl -> webClient.post()
                        .uri(fullUrl)
                        .headers(headers == null ? EMPTY_HEADERS : headers)
                        .bodyValue(body)
                        .accept(MediaType.TEXT_EVENT_STREAM)
                        .retrieve()
                        .bodyToFlux(tClass)
                        .filter(Objects::nonNull)  // 过滤空行
                        .doOnSubscribe(subscription -> log.info("开始接收SSE流: {}", fullUrl))
                        .doOnComplete(() -> log.info("SSE流接收完成: {}", fullUrl))
                        .doOnError(error -> log.error("SSE流接收错误: {}, URL: {}", error.getMessage(), fullUrl))
                        .retryWhen(Retry.backoff(3, Duration.ofMillis(1000))
                                .doBeforeRetry(retry -> log.warn("SSE请求正在重试 (第{}次): {}", retry.totalRetries() + 1, fullUrl))
                                .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> new CustomException("重试后失败: " + fullUrl))));// 重试3次，间隔1秒
    }

    /**
     * 处理 uri地址
     *
     * @param uri
     * @return
     */
    private static Mono<URI> getFullUrl(ServiceEnum serviceEnum, String uri) {
        return Mono.fromCallable(() -> URI.create(serviceEnum.getUrl()).resolve(uri))
                .doOnError(error -> log.error("处理URL失败: {}", error.getMessage()));
    }

}
